﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Xml;
using System.Reflection;
using System.IO;
using System.Security;

namespace Copy4Backup
{
	/// <summary>
	/// Interaction logic for MainWindow.xaml
	/// </summary>
	public partial class MainWindow : Window
	{
		#region Fields
		private readonly String fileName_;
		#region Constants

		private const String ElementCopy4Backup = "Copy4Backup";
		private const String ElementVersion = "Version";
		private const String ElementProjects = "Projects";
		private const String ElementProject = "Project";
		private const String ElementName = "Name";
		private const String ElementSourceFolders = "SourceFolders";
		private const String ElementSourceFolder = "SourceFolder";
		private const String ElementDestinationFolder = "DestinationFolder";
		private const String ElementFilters = "Filters";
		private const String ElementFolderFilters = "FolderFilters";
		private const String ElementFolderFilter = "FolderFilter";
		private const String ElementFileFilters = "FileFilters";
		private const String ElementFileFilter = "FileFilter";

		private const String AttrPattern = "pattern";
		private const String AttrAction = "action";

		private const String CurrentVersion = "1.0";
		#endregion Constants

		private readonly ObservableCollection<Project> projects_;
		#endregion Fields

		public MainWindow()
		{
			InitializeComponent();

			projects_ = new ObservableCollection<Project>();
			listBoxProjects.ItemsSource = projects_;

			fileName_ = System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
			fileName_ += "\\" + Assembly.GetExecutingAssembly().GetName().Name;

			if (!Directory.Exists(fileName_))
			{
				try
				{
					Directory.CreateDirectory(fileName_);
				}

				catch (UnauthorizedAccessException excp)
				{
					MessageBox.Show(String.Format("You have not required permissions to create folder \"{0}\": {1}",
						fileName_, excp.Message), Title, MessageBoxButton.OK, MessageBoxImage.Error);
				}

				catch (ArgumentException excp)
				{
					MessageBox.Show(String.Format("The path \"{0}\" is not valid: {1}",
						fileName_, excp.Message), Title, MessageBoxButton.OK, MessageBoxImage.Error);
				}

				catch (PathTooLongException excp)
				{
					MessageBox.Show(String.Format("The specified folder \"{0}\" exceed the system-defined maximum length: {1}",
						fileName_, excp.Message), Title, MessageBoxButton.OK, MessageBoxImage.Error);
				}

				catch (DirectoryNotFoundException excp)
				{
					MessageBox.Show(String.Format("The path \"{0}\" is not valid: {1}",
						fileName_, excp.Message), Title, MessageBoxButton.OK, MessageBoxImage.Error);
				}

				catch (NotSupportedException excp)
				{
					MessageBox.Show(String.Format("The path \"{0}\" contains a colon character (:) that is not part of a drive label (e.g. \"C:\\\"): {1}",
						fileName_, excp.Message), Title, MessageBoxButton.OK, MessageBoxImage.Error);
				}

				catch (IOException excp)
				{
					MessageBox.Show(String.Format("Can't create folder \"{0}\". Error: {1}",
						fileName_, excp.Message),
						Title, MessageBoxButton.OK, MessageBoxImage.Error);
				}

			}

			fileName_ += "\\" + Assembly.GetExecutingAssembly().GetName().Name + ".xml";
		}

		private void buttonAdd_Click(object sender, RoutedEventArgs e)
		{
			Project project = new Project();

			ProjectWindow pw = new ProjectWindow(project);

			if ((bool)pw.ShowDialog())
			{
				projects_.Add(project);
			}
		}

		private void buttonEdit_Click(object sender, RoutedEventArgs e)
		{
			int index = listBoxProjects.SelectedIndex;
			Project project = listBoxProjects.SelectedItem as Project;

			if (null != project)
			{
				ProjectWindow pw = new ProjectWindow(project);
				if ((bool)pw.ShowDialog())
				{
					projects_.RemoveAt(index);
					projects_.Insert(index, project);
				}
			}

		}

		private void buttonDelete_Click(object sender, RoutedEventArgs e)
		{
			int index = listBoxProjects.SelectedIndex;

			if (0 <= index)
			{
				projects_.RemoveAt(index);
			}
		}

		private void buttonExecute_Click(object sender, RoutedEventArgs e)
		{
			Project project = listBoxProjects.SelectedItem as Project;

			if (null != project)
			{
				ExecuteWindow ew = new ExecuteWindow(project);

				this.Hide();

				ew.ShowDialog();

				this.Show();
			}

		}

		private void Window_Loaded(object sender, RoutedEventArgs e)
		{
			LoadProjects();
		}

		private void LoadProjects()
		{
			XmlDocument doc = new XmlDocument();

			try
			{
				doc.Load(fileName_);

				XmlNode nRoot = doc.SelectSingleNode(ElementCopy4Backup);

				if (null != nRoot)
				{
					XmlNode nodeVersion = nRoot.SelectSingleNode(ElementVersion);
					if (null != nodeVersion)
					{
						foreach (XmlNode projectNode in nRoot.SelectNodes(ElementProjects + "/" + ElementProject))
						{
							Project project = new Project();

							XmlNode n;

							n = projectNode.SelectSingleNode(ElementName);

							if (null != n && null != n.InnerText)
							{
								project.Name = n.InnerText;
							}

							foreach (XmlNode nodeSourceFolder in projectNode.SelectNodes(ElementSourceFolders + "/" + ElementSourceFolder))
							{
								if (null != nodeSourceFolder.InnerText)
								{
									project.AddSourceFolder(nodeSourceFolder.InnerText);
								}
							}

							n = projectNode.SelectSingleNode(ElementDestinationFolder);

							if (null != n && null != n.InnerText)
							{
								project.DestinationFolder = n.InnerText;
							}

							foreach (XmlNode folderFilter in projectNode.SelectNodes(ElementFilters + "/" + ElementFolderFilters + "/" + ElementFolderFilter))
							{
								if (null != folderFilter.Attributes[AttrPattern])
								{
									project.AddFolderFilter(folderFilter.Attributes[AttrPattern].Value);
								}
							}

							foreach (XmlNode fileFilter in projectNode.SelectNodes(ElementFilters + "/" + ElementFileFilters + "/" + ElementFileFilter))
							{
								if (null != fileFilter.Attributes[AttrPattern])
								{
									project.AddFileFilter(fileFilter.Attributes[AttrPattern].Value);
								}
							}

							if (!CurrentVersion.Equals(nodeVersion.Value))
							{
								// Here will be version diff
							}

							// Validate

							if (0 >= project.SourceFolders.Count
								|| null == project.DestinationFolder || 0 == project.DestinationFolder.Length)
							{
								// Data is not valid.
								System.Diagnostics.Debug.WriteLine("Data is not valid");
							}

							if (true)
							{
								projects_.Add(project);
							}
						}
					}
					else
					{
						// Version is not specified.
						System.Diagnostics.Debug.WriteLine("Version is not specified");
					}
				}
				else
				{
					// It is not ElementCopy4Backup document.
					System.Diagnostics.Debug.WriteLine(String.Format("The root element is not {0}", ElementCopy4Backup));
				}
			}

			catch (XmlException excp)
			{
				MessageBox.Show(String.Format("Parse or load error for \"{0}\". Error: {1}",
						fileName_, excp.Message),
						Title, MessageBoxButton.OK, MessageBoxImage.Error);
			}

			catch (ArgumentException excp)
			{
				MessageBox.Show(String.Format("The \"{0}\" file name is not valid. Error: {1}",
						fileName_, excp.Message),
						Title, MessageBoxButton.OK, MessageBoxImage.Error);
			}

			catch (PathTooLongException excp)
			{
				MessageBox.Show(String.Format("The path \"{0}\" exceed the system-defined maximum length. Error: {1}",
						fileName_, excp.Message),
						Title, MessageBoxButton.OK, MessageBoxImage.Error);
			}

			catch (DirectoryNotFoundException excp)
			{
				MessageBox.Show(String.Format("The \"{0}\" file name is not valid. Error: {1}",
						fileName_, excp.Message),
						Title, MessageBoxButton.OK, MessageBoxImage.Error);
			}

			catch (FileNotFoundException)
			{
				// It is o.k.
			}

			catch (UnauthorizedAccessException excp)
			{
				MessageBox.Show(String.Format("Unauthorized access to file \"{0}\". Error: {1}",
						fileName_, excp.Message),
						Title, MessageBoxButton.OK, MessageBoxImage.Error);
			}

			catch (NotSupportedException excp)
			{
				MessageBox.Show(String.Format("The file name \"{0}\" is in invalid format. Error: {1}",
						fileName_, excp.Message),
						Title, MessageBoxButton.OK, MessageBoxImage.Error);
			}

			catch (SecurityException excp)
			{
				MessageBox.Show(String.Format("You have not permission to read \"{0}\". Error: {1}",
						fileName_, excp.Message),
						Title, MessageBoxButton.OK, MessageBoxImage.Error);
			}

			catch (IOException excp)
			{
				MessageBox.Show(String.Format("An I/O error occurred while opening the file \"{0}\". Error: {1}",
						fileName_, excp.Message),
						Title, MessageBoxButton.OK, MessageBoxImage.Error);
			}
		}

		private void SaveProjects()
		{
			XmlWriterSettings ws = new XmlWriterSettings();
			ws.Indent = true;

			using (XmlWriter writer = XmlWriter.Create(fileName_, ws))
			{
				writer.WriteStartDocument();

				writer.WriteStartElement(ElementCopy4Backup);

				writer.WriteStartElement(ElementVersion);
				writer.WriteValue(CurrentVersion);
				writer.WriteEndElement();	//ElementVersion

				writer.WriteStartElement(ElementProjects);
				foreach (Project project in projects_)
				{
					writer.WriteStartElement(ElementProject);

					writer.WriteStartElement(ElementName);
					writer.WriteValue(project.Name);
					writer.WriteEndElement();	//ElementName

					writer.WriteStartElement(ElementSourceFolders);
					foreach (String folder in project.SourceFolders)
					{
						writer.WriteStartElement(ElementSourceFolder);
						writer.WriteValue(folder);
						writer.WriteEndElement();	//ElementSourceFolder
					}
					writer.WriteEndElement();	//ElementSourceFolders

					writer.WriteStartElement(ElementDestinationFolder);
					writer.WriteValue(project.DestinationFolder);
					writer.WriteEndElement();	//ElementDestinationFolder

					writer.WriteStartElement(ElementFilters);

					writer.WriteStartElement(ElementFolderFilters);
					foreach (String folderFilter in project.FolderFilters)
					{
						writer.WriteStartElement(ElementFolderFilter);
						writer.WriteAttributeString(AttrPattern, folderFilter);
						writer.WriteAttributeString(AttrAction, "exclude");
						writer.WriteEndElement();	//ElementFolderFilter
					}
					writer.WriteEndElement();	//ElementFolderFilters

					writer.WriteStartElement(ElementFileFilters);
					foreach (String fileFilter in project.FileFilters)
					{
						writer.WriteStartElement(ElementFileFilter);
						writer.WriteAttributeString(AttrPattern, fileFilter);
						writer.WriteAttributeString(AttrAction, "exclude");
						writer.WriteEndElement();	//ElementFileFilter
					}
					writer.WriteEndElement();	//ElementFileFilters

					writer.WriteEndElement();	//ElementFilters

					writer.WriteEndElement();	//ElementProject
				}
				writer.WriteEndElement();	//ElementProjects

				writer.WriteEndElement();	//ElementCopy4BackupProject

				writer.WriteEndDocument();
			}
		}

		private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
		{
			SaveProjects();
		}

		private void buttonAbout_Click(object sender, RoutedEventArgs e)
		{
			(new AboutWindow()).ShowDialog();
		}
	}
}
